﻿
using NicoView.Model.Service.Events;
using NicoView.Model.Service.Results;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace NicoView.Model.Service.Comments
{
    public enum NicoCommentSendResut
    {
        Sent,
        EnoughLength,
        Sending,
        SameAsBeforeComment,
        Unknown
    }

    public abstract class NicoCommentClientBase:INicoCommentClient
    {
        public abstract NicoServiceBase Service
        {
            get;
        }

        public NicoCommentClientBase()
        {
            InternalColors = new List<INicoCommentColor>();
            InternalSizings = new List<INicoCommentSize>();
            InternalPositions = new List<INicoCommentPosition>();
            this.IsSendding = false;
           
        }

        private NotifyNicoComment beforeComment;

        protected internal void OnNicoCommentReceivePeriod(NicoCommentReceivePeriodEventArgs args)
        {
            if (ReceivePeriod != null) ReceivePeriod(args);
        }

        protected internal void OnNicoCommentReceive(NicoCommentReceiveEventArgs args)
        {
            var beforeComments = from comment in args.Comments
                                 where (
                                        (comment.User.Id == this.Service.BaseApplication.Instance.SelfUser.Id) || 
                                        (this.Service.ServiceInfo.IsOwner && comment.UserType == CommentUserType.Owner)
                                    ) && ((beforeComment == null) ||( beforeComment.Date < comment.Date))
                                 orderby comment.Date descending
                                 select comment;

            if (beforeComments.Count() > 0)
            {
                this.beforeComment = beforeComments.First();
            }

            if (Received != null) Received(args);
        }

        protected internal void OnNicoCommentGetUserRequest(NicoCommentGetUserRequestEventArgs args)
        {
            if (UserInfoRequest != null) UserInfoRequest(args);
        }

        protected internal void OnNicoCommentDisoconnectRequest(NicoCommentDisconnectEventArgs args)
        {
            if (Disconnected != null) Disconnected(args);
        }

        protected internal void OnNicoCommentChatResult(NicoCommentSentResultEventArgs args)
        {
            if (SentResult != null) SentResult(args);
        }

        protected internal void OnNicoCommentThreadReceived(NicoCommentThreadEventArgs args)
        {
            if (ThreadReceived != null) ThreadReceived(args);
        }

        public event NicoCommentReceivePeriodHandler ReceivePeriod;

        public event NicoCommentReceiveHandler Received;

        public event NicoCommentGetUserRequestHandler UserInfoRequest;

        public event NicoCommentDisconnectRequestHandler Disconnected;

        public event NicoCommentSentResultHandler SentResult;

        public event NicoCommentThreadHandler ThreadReceived;

        public abstract event NicoView.Model.Service.Lives.NicoLiveCommentClient.NicoLiveHeartBeatHandler HeartBeatReceived;

        public abstract ICollection<NotifyNicoComment> Comments
        {
            get;
        }

        public async Task<Result<NicoCommentSendResut>> SendAsync(NicoSendComment comment)
        {
            if (comment.Body.Length <= 0) return new Result<NicoCommentSendResut>(NicoCommentSendResut.EnoughLength, false, "");

            
            

            
            
            
            if (this.Service.BaseApplication.Property.NgAvoidance) this.Service.BaseApplication.Property.SubmitCommentConversions.Convert(comment);

            comment.Body = comment.Body.Replace("\r\n", "\n");

            
            
            
            if (this.IsSendding == true) 
            {
                    
                return new Result<NicoCommentSendResut>(NicoCommentSendResut.Sending, false, "");
            }

               
            
            
            
            
            if ((this.beforeComment != null) && (this.beforeComment.BaseBody == comment.Body) && !comment.IsOwner)
            {
                return new Result<NicoCommentSendResut>(NicoCommentSendResut.SameAsBeforeComment, false, "前回送信したコメントを続けて送信することはできません");
            }
            
            

            try
            {
                
                this.IsSendding = true;
                var result = await OnSendAsync(comment).ConfigureAwait(false);

                if (result.Succeed)
                {
                   
                    return new Result<NicoCommentSendResut>(NicoCommentSendResut.Sent, true, "");
                }
                else
                {
                    return new Result<NicoCommentSendResut>(NicoCommentSendResut.Unknown, false, "");
                }

            }
            finally
            {
                this.IsSendding = false;
            }

            
        }


   



        protected abstract Task<IResult> OnSendAsync(NicoSendComment comment);

        public abstract Task WayBacRequestkAsync(int count);

        public abstract void Dispose();


        public abstract INicoCommentClientInfo Info
        {
            get;
        }


        public abstract int CalcActiveWatchCount(TimeSpan elapsed);

        internal protected List<INicoCommentColor> InternalColors;

        public ICollection<INicoCommentColor> Colors
        {
            get
            {
                return InternalColors;
            }
        }


        internal protected List<INicoCommentSize> InternalSizings;

        public ICollection<INicoCommentSize> Sizings
        {
            get
            {
                return InternalSizings;
            }
        }

        internal protected List<INicoCommentPosition> InternalPositions;

        public ICollection<INicoCommentPosition> Positions
        {
            get
            {
                return InternalPositions;
            }
        }


        public abstract bool Connected
        {
            get;
        }








        public bool IsSendding
        {
            get;
            internal set;
        }
    }
}
